package com.insight.common.mq.utils;

import cn.hutool.core.net.NetUtil;
import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.Digester;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author JinPengDong
 * @date 2023/10/21
 */
@Slf4j
public class MachineCodeUtil {
    public static final String WINDOWS_OS_NAME = "WINDOWS";
    public static final String SYSTEM_PROPERTY_OS_NAME = "os.name";

    private static String machineCode = "";

    /**
     * 获取机器唯一识别码（CPU ID + BIOS UUID）
     *
     * @return 机器唯一识别码
     */
    public static String getMachineCode() {
        if (StringUtils.isNotBlank(machineCode)) {
            return machineCode;
        } else {
            String mc = "";
            try {
                String mac = getMac();
                String cpu = getCpuId();
                String bios = getBiosUuid();
                String board = getBoardSerial();
                // 只有非空才拼接
                mc = (StringUtils.isNotBlank(mac) ? mac : "")
                        + (StringUtils.isNotBlank(cpu) ? cpu : "")
                        + (StringUtils.isNotBlank(bios) ? bios : "")
                        + (StringUtils.isNotBlank(board) ? board : "");
            } catch (Exception e) {
                log.warn("获取硬件信息异常，将使用随机 UUID", e);
            }
            if (StringUtils.isBlank(mc)) {
                mc = UUID.randomUUID().toString();
            }
            machineCode = shortMd5(mc);
            return machineCode;
        }
    }

    public static String getMac() {
        try {
            final List<InetAddress> localAllInetAddress = getLocalAllInetAddress();
            final List<String> macs = localAllInetAddress.stream().map(NetUtil::getMacAddress).distinct().collect(Collectors.toList());
            return String.join("", macs);
        } catch (SocketException e) {
            return "";
        }

    }

    public static List<InetAddress> getLocalAllInetAddress() throws SocketException {
        List<InetAddress> result = new ArrayList<>(4);
        for (Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements(); ) {
            NetworkInterface iface = networkInterfaces.nextElement();
            for (Enumeration<InetAddress> inetAddresses = iface.getInetAddresses(); inetAddresses.hasMoreElements(); ) {
                InetAddress inetAddr = inetAddresses.nextElement();
                if (!inetAddr.isLoopbackAddress()
                        && !inetAddr.isLinkLocalAddress() && !inetAddr.isMulticastAddress()) {
                    result.add(inetAddr);
                }
            }
        }
        return result;
    }

    /**
     * java 实现Md5
     */
    /**
     * 直接加密
     *
     * @param data 要加密的数据
     * @return 加密后的结果
     */
    public static String encode(String data) {
        Digester md5 = new Digester(DigestAlgorithm.MD5);
        return md5.digestHex(data);
    }

    public static String shortMd5(String data) {
        final String encode = encode(data);
        return encode.substring(8, 24).toUpperCase();
    }


    /**
     * 获取当前系统CPU序列，可区分linux系统和windows系统
     */
    public static String getCpuId() throws IOException {
        String cpuId;
        // 获取当前操作系统名称
        String os = System.getProperty(SYSTEM_PROPERTY_OS_NAME);
        os = os.toUpperCase();

        if (os.startsWith(WINDOWS_OS_NAME)) {
            cpuId = executeWindowsCmd(new String[]{"wmic", "cpu", "get", "ProcessorId"});
        } else {
            cpuId = getLinuxDecodeInfo("dmidecode -t processor | grep 'ID'", "ID", ":");
        }
        return cpuId;
    }

    /**
     * 获取 BIOS UUID
     *
     * @return BIOS UUID
     * @throws IOException 获取BIOS UUID期间的IO异常
     */
    public static String getBiosUuid() throws IOException {
        String biosUuid;
        // 获取当前操作系统名称
        String os = System.getProperty("os.name");
        os = os.toUpperCase();

        if (os.startsWith(WINDOWS_OS_NAME)) {
            biosUuid = executeWindowsCmd(new String[]{"wmic", "path", "win32_computersystemproduct", "get", "uuid"});
        } else {
            biosUuid = getLinuxDecodeInfo("dmidecode -t system | grep 'UUID'", "UUID", ":");
        }
        return biosUuid;
    }

    /**
     * 获取 boardSerial
     *
     * @return boardSerial
     * @throws IOException 获取boardSerial期间的IO异常
     */
    public static String getBoardSerial() throws IOException {
        String boardSerial;
        // 获取当前操作系统名称
        String os = System.getProperty("os.name");
        os = os.toUpperCase();

        if (os.startsWith(WINDOWS_OS_NAME)) {
            boardSerial = executeWindowsCmd(new String[]{"wmic", "baseboard", "get", "serialnumber"});
        } else {
            boardSerial = getLinuxDecodeInfo("dmidecode -t baseboard | grep 'Serial Number'", "Serial Number", ":");
        }
        return boardSerial;
    }


    /**
     * 获取linux系统
     * dmidecode
     * 命令的信息
     */
    public static String getLinuxDecodeInfo(String cmd, String record, String symbol) throws IOException {
        String execResult = executeLinuxCmd(cmd);
        String[] infos = execResult.split("\n");
        for (final String info : infos) {
            String trimmedInfo = info.trim();
            if (trimmedInfo.contains(record)) {
                trimmedInfo = trimmedInfo.replace(" ", "");
                String[] sn = trimmedInfo.split(symbol);
                return sn[1].toUpperCase();
            }
        }
        return "";
    }


    /**
     * 执行Linux 命令
     *
     * @param cmd Linux 命令
     * @return 命令结果信息
     * @throws IOException 执行命令期间发生的IO异常
     */
    public static String executeLinuxCmd(String cmd) throws IOException {
        Runtime run = Runtime.getRuntime();
        Process process;
        String[] shell = {"/bin/bash", "-c", cmd};
        process = run.exec(shell);
        InputStream processInputStream = process.getInputStream();
        StringBuilder stringBuilder = new StringBuilder();
        byte[] b = new byte[8192];
        for (int n; (n = processInputStream.read(b)) != -1; ) {
            stringBuilder.append(new String(b, 0, n));
        }
        processInputStream.close();
        process.destroy();
        return stringBuilder.toString();
    }

    public static String executeWindowsCmd(String[] cmd) throws IOException {
        Process process = Runtime.getRuntime().exec(cmd);
        process.getOutputStream().close();
        Scanner sc = new Scanner(process.getInputStream());
        sc.next();
        return sc.next().toUpperCase().replace(" ", "");
    }
}

